package fclient

import (
	"dmsv/common"
	"errors"
	"github.com/beego/beego/v2/core/logs"
	"github.com/g2fly99/esl"
	"i-hea/utils/httplib"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	cCallOut       = "outbound"
	cEventFilter   = "CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_PROGRESS CHANNEL_PROGRESS_MEDIA CHANNEL_HANGUP "
	cEventFilterEx = "HEARTBEAT BACKGROUND_JOB PLAYBACK_STOP CHANNEL_DESTROY DTMF RECORD_START"
	//cEventFilter     = `ALL`
	cOutCallMaxRetry = 3
)

const (
	CALL_EVENT_KEY        = "eKey"
	CALL_EVENT_PLAYOVER   = "ePlayover"
	CALL_EVENT_RECORD     = "eRecord"
	CALL_EVENT_CREATE     = "eCreate"
	CALL_EVENT_ANSWER     = "answer"
	CALL_EVENT_RING       = "eRing"
	CALL_EVENT_DISCONNECT = "eDisconnect"
	CALL_EVENT_DESTROY    = "eDestroy"
	CALL_EVENT_FAIL       = "eFail"
)

type HangUpInfo struct {
	SipCode     string
	Describtion string
}

type EventInfo struct {
	Name   string
	CallId string
	EvTime time.Time
	Data   interface{}
}

var gHandler *Handler

type eventInfo struct {
	event *esl.Event
	retry int
	begin time.Time
}

type Handler struct {
	bgJob         sync.Map
	bgJobEvt      chan *eventInfo
	fsHost        string
	pwd           string
	conn          *esl.Connection
	httpClient    *httplib.HttpClient
	heartbeatChan chan *esl.Event
	callMsg       chan *EventInfo
}

func (h *Handler) keepAlive() {

	alive := time.Now()
	timeOutChan := make(chan bool, 5)
	timeOutChan <- true

	logs.Debug("启动 fs 活动检测线程.....")

	for {
		select {
		case <-h.heartbeatChan:
			alive = time.Now()

		case <-timeOutChan:

			if time.Since(alive) > 43*time.Second {
				logs.Error("fs is down,last alive:%v...", alive.Format("2006-01-02 15:04:05.000"))
				if h.conn != nil && h.conn.Connected {
					h.conn.Close()
				}
			}
			//发送计时报文
			time.AfterFunc(5*time.Second, func() { timeOutChan <- true })
		}
	}
}

func (h *Handler) OnConnect(con *esl.Connection) {
	logs.Debug("connect to freeswitch[%v] successful", h.fsHost)
	con.SendRecv("event", "plain", cEventFilter)
	con.SendRecv("event", "plain", cEventFilterEx)

	h.heartbeatChan <- nil
}

func (h *Handler) OnDisconnect(con *esl.Connection, ev *esl.Event) {
	logs.Debug("esl disconnected:", ev)
	h.conn.Close()
}

func (h *Handler) OnClose(con *esl.Connection) {

	logs.Debug("esl connection closed,now reconnecting...")
	for {

		newCon, err := esl.NewConnection(h.fsHost, h.pwd, h)
		if err != nil {
			logs.Error("ERR connecting to freeswitch:", err)
			time.Sleep(2 * time.Second)
			continue
		}

		h.conn = newCon
		break
	}

	go h.conn.HandleEvents()
}

func (h *Handler) getCallNumber() int {

	res, err := h.conn.Api("show", "channels", "count")
	if err != nil {
		logs.Error("get calls err:%v", err)
		return 0
	}

	res = strings.ReplaceAll(res, "\n", "")
	r := strings.Split(res, " ")

	number, err := strconv.Atoi(r[0])
	if err != nil {
		logs.Error("strconv wrong:%v", err)
	}

	return number
}

func (h *Handler) getRegSipNumber() int {

	res, err := h.conn.Api("show", "registrations")
	if err != nil {
		logs.Error("get calls err:%v", err)
		return 0
	}

	rs := strings.Split(res, "\n")
	for _, s := range rs {
		if strings.Contains(s, "total") {
			t := strings.Split(s, " ")
			number, err := strconv.Atoi(t[0])
			if err != nil {
				logs.Error("strconv wrong:%v", err)
			}
			return number
		}
	}

	logs.Debug("get reg sip :%v", res)
	return 0
}

func (h *Handler) bgJobTask() {

	for {

		newEvnt := <-h.bgJobEvt

		jbId := newEvnt.event.Get("Job-Uuid")

		value, exist := h.bgJob.Load(jbId)
		logs.Debug("bgjobId:%v, result:%s ,%v  ", jbId, newEvnt.event.GetTextBody(), exist)
		if false == exist {
			//说明还未来得保存
			logs.Error("jobId:%v 还未存储,retry:%v", jbId, newEvnt.retry)
			if newEvnt.retry > 10 {
				continue
			}

			go func() {

				time.Sleep(300 * time.Millisecond)
				newEvnt.retry++
				h.bgJobEvt <- newEvnt
			}()
			continue
		}

		callId := value.(string)

		h.bgJob.Delete(jbId)

		result := newEvnt.event.GetTextBody()
		msg := &EventInfo{
			CallId: callId,
			Name:   CALL_EVENT_FAIL,
		}

		if strings.Contains(result, "USER_NOT_REGISTERED") {
			msg.Data = common.ErrSipNotReg
		} else if strings.Contains(result, "SUBSCRIBER_ABSENT") {
			msg.Data = common.ErrSipNotExist
		} else if strings.Contains(result, "DESTINATION_OUT_OF_ORDER") {
			msg.Data = common.ErrSipNetWork
		} else if strings.Contains(result, "NORMAL_CLEARING") {
			msg.Data = common.SuccCancelByCaller
		} else if strings.Contains(result, "NO_ANSWER") {
			msg.Data = common.ErrServiceNoAnswer
		} else if strings.Contains(result, "NORMAL_CLEARING") {
			msg.Data = common.ErrCallerHangup
		} else if strings.Contains(result, "ERR") {
			msg.Data = common.ErrServiceUnreach
		} else if strings.Contains(result, "OK") {
			//成功不需要处理 不发送消息
			continue
		}

		gHandler.callMsg <- msg
	}
}

func (h *Handler) addNewbgJobEvnt(ev *esl.Event) {

	jbId := ev.Get("Job-Uuid")
	logs.Debug("bgjobId:%v, result:%s", jbId, ev.GetTextBody())
	if len(h.bgJobEvt) > 1000 {
		logs.Error("bgjob[%v] event chan is full,drop message:%v", jbId, ev.GetTextBody())
		return
	}

	newEvnt := &eventInfo{
		event: ev,
		begin: time.Now(),
	}

	h.bgJobEvt <- newEvnt
}

func timeStampToTime(timeStamp string) time.Time {

	t, err := strconv.Atoi(timeStamp)
	if err != nil {
		return time.Now()
	}
	tSec := int64(t / 1000000)
	tMsec := int64(t % 1000000 * 1000)

	return time.Unix(tSec, tMsec)
}

func (h *Handler) OnEvent(con *esl.Connection, ev *esl.Event) {

	if ev.Name == esl.RE_SCHEDULE {
		return
	}

	if ev.Name == esl.HEARTBEAT {
		h.heartbeatChan <- ev
		return
	}

	t := timeStampToTime(ev.Get("Event-Date-Timestamp"))

	logs.Debug("%s %s event:%s.callDirection:%s,Channel-State:%s",
		t.Format("15:04:05.000"), ev.UId,
		ev.Name, ev.Get("Call-Direction"),
		ev.Get("Channel-State"))

	//jobId 单独处理
	if esl.BACKGROUND_JOB == ev.Name {
		h.addNewbgJobEvnt(ev)
		return
	}

	msg := &EventInfo{
		CallId: ev.UId,
		EvTime: t,
		Data:   ev,
	}

	switch ev.Name {
	case esl.CHANNEL_CREATE:
		//创建呼叫成功
		msg.Name = CALL_EVENT_CREATE

	case esl.CHANNEL_PROGRESS:
		if ev.Get("Call-Direction") == cCallOut {
			logs.Debug("call channel progress:%s", ev.GetTextBody())
		}

		msg.Name = CALL_EVENT_RING
	case esl.CHANNEL_PROGRESS_MEDIA:
		if ev.Get("Call-Direction") == cCallOut {
			logs.Debug("call channel early media:%s", ev.GetTextBody())
		}
		msg.Name = CALL_EVENT_RING
	case esl.CHANNEL_ANSWER:
		msg.Name = CALL_EVENT_ANSWER
	case esl.CHANNEL_PARK:

	case esl.CHANNEL_UNPARK:

	case esl.DTMF:
		msg.Name = CALL_EVENT_KEY
		msg.Data = ev.Get("DTMF-Digit")

	case esl.RECORD_START:
		msg.Name = CALL_EVENT_RECORD
	case esl.RECORD_STOP:
	case esl.PLAYBACK_START:

	case esl.PLAYBACK_STOP:
		//	logs.Debug(ev)
		//filePath := ev.Get("Playback-File-Path")
		msg.Name = CALL_EVENT_PLAYOVER
	case esl.CHANNEL_CALLSTATE:

		//logs.Debug(ev)
	case esl.CHANNEL_HANGUP:

		data := HangUpInfo{
			SipCode:     ev.Get("variable_sip_term_status"),
			Describtion: ev.Get("variable_sip_invite_failure_phrase"),
		}
		msg.Data = data
		msg.Name = CALL_EVENT_DISCONNECT

	case esl.CHANNEL_DESTROY:
		data := HangUpInfo{
			SipCode:     ev.Get("variable_sip_term_status"),
			Describtion: ev.Get("variable_sip_invite_failure_phrase"),
		}
		msg.Data = data
		msg.Name = CALL_EVENT_DESTROY
	default:
		logs.Debug("Event not watch:%v", ev)
	}
	gHandler.callMsg <- msg
}

//挂机
//放音
//放音获取按键
//呼叫本地坐席
func (h *Handler) cmdRun() {

}

func NewFsConn(host, pwd string, evntChan chan *EventInfo) error {

	if evntChan == nil {
		return errors.New("事件通道未初始化....")
	}

	handler := &Handler{
		bgJobEvt:      make(chan *eventInfo, 1000),
		heartbeatChan: make(chan *esl.Event, 1000),
		callMsg:       evntChan,
	}

	con, err := esl.NewConnection(host, pwd, handler)
	if err != nil {
		logs.Error("ERR connecting to freeswitch[%v]:%v", host, err)
		return err
	}

	handler.conn = con

	handler.fsHost = host
	handler.pwd = pwd

	go con.HandleEvents()
	go handler.bgJobTask()
	go handler.keepAlive()

	gHandler = handler

	return nil
}
